﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "SurfaceAndTerminals.h"

#include "GbmpConstants.h"
#include "Vector3dUtils.h"
#include "ISurface.h"
#include "IPlane.h"
#include "ICylinder.h"
#include "ILine2d.h"
#include "IArc2d.h"
#include "IElement.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

DATA_DEFINE(TerminalParams)
{
    this->SetStartRangeU__(0);
    this->SetMidRangeU__(0);
    this->SetEndRangeU__(0);
    this->SetStartRangeV__(0);
    this->SetMidRangeV__(0);
    this->SetEndRangeV__(0);
}

TerminalParams TerminalParams::Create(double startRangeU, double midRangeU, double endRangeU, double startRangeV, double midRangeV, double endRangeV)
{
    TerminalParams ret;
    ret.SetStartRangeU(startRangeU);
    ret.SetMidRangeU(midRangeU);
    ret.SetEndRangeU(endRangeU);
    ret.SetStartRangeV(startRangeV);
    ret.SetMidRangeV(midRangeV);
    ret.SetEndRangeV(endRangeV);
    return ret;
}

Intervald gcmp::TerminalParams::GetRangeU(bool & bSameDirection) const
{
    const double start = this->GetStartRangeU();
    const double end =this->GetEndRangeU();

    Intervald ret = Intervald::Create(start, end);
    bSameDirection = !ret.IsEmpty();
    if(ret.IsEmpty())
    {
        ret = Intervald::Create(end, start);
    }

    return ret;
}

Intervald gcmp::TerminalParams::GetRangeV(bool & bSameDirection) const
{
    const double start = this->GetStartRangeV();
    const double end = this->GetEndRangeV();

    Intervald ret = Intervald::Create(start, end);
    bSameDirection = !ret.IsEmpty();
    if (ret.IsEmpty())
    {
        ret = Intervald::Create(end, start);
    }

    return ret;
}

std::wstring gcmp::TerminalParams::GetRangeUDebugString() const
{
    return L"[ "
        + StringUtil::ToWString(this->GetStartRangeU()) + L", "
        + StringUtil::ToWString(this->GetMidRangeU()) + L", "
        + StringUtil::ToWString(this->GetEndRangeU()) + L" ]"
        + L"\n";
}

std::wstring gcmp::TerminalParams::GetRangeVDebugString() const
{
    return L"[ "
        + StringUtil::ToWString(this->GetStartRangeV()) + L", "
        + StringUtil::ToWString(this->GetMidRangeV()) + L", "
        + StringUtil::ToWString(this->GetEndRangeV()) + L" ]"
        + L"\n";
}

DATA_DEFINE(SurfaceAndTerminals) 
{
}

gcmp::OwnerPtr<gcmp::SurfaceAndTerminals> SurfaceAndTerminals::Create(
    const ICurve2d* pCurve2d, const Intervald& rangeV, const Vector3d& dirV /*= Vector3d::UnitZ*/)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pCurve2d && !rangeV.IsEmpty(), L"invalid params",L"GDMPLab",L"2024-03-30");
    OwnerPtr<gcmp::SurfaceAndTerminals> opRet = nullptr;
    if(const ILine2d* pLine2d = quick_cast<ILine2d>(pCurve2d))
    {
        opRet = CreateAsPlane(pLine2d, rangeV, dirV);
    }
    else if(const IArc2d* pArc2d = quick_cast<IArc2d>(pCurve2d))
    {
        opRet = CreateAsCylinder(pArc2d, rangeV, dirV);
    }
    else
    {
        DBG_WARN(L"仅支持平面或圆柱面。",L"GDMPLab",L"2024-03-30");
    }

    return opRet;
} 

OwnerPtr<SurfaceAndTerminals> SurfaceAndTerminals::CreateAsPlane(
    const ILine2d * pLine2d, const Intervald & rangeV, const Vector3d& dirV /*= Vector3d::UnitZ*/)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLine2d && !rangeV.IsEmpty(), L"invalid params",L"GDMPLab",L"2024-03-30");

    OwnerPtr<SurfaceAndTerminals> opRet = NEW_AS_OWNER_PTR(SurfaceAndTerminals);
    opRet->SetDatumSurface__(IPlane::Create(
        Vector3d(pLine2d->GetOrigin(), 0),
        Vector3d(pLine2d->GetDirection(), 0),
        dirV)
    );

    const Intervald& rangeU = pLine2d->GetLimits();
    opRet->SetGlobalTerminalParams(
        TerminalParams::Create(
            rangeU.GetMin(), rangeU.Middle(), rangeU.GetMax(), 
            rangeV.GetMin(), rangeV.Middle(), rangeV.GetMax()
        )
    );

    return opRet;
}

OwnerPtr<SurfaceAndTerminals> SurfaceAndTerminals::CreateAsCylinder(
    const IArc2d * pArc2d, const Intervald & rangeV, const Vector3d& dirV /*= Vector3d::UnitZ*/)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pArc2d && !rangeV.IsEmpty(), L"invalid params",L"GDMPLab",L"2024-03-30");

    Coordinate3d coord(Vector3d(pArc2d->GetStartPoint(), 0), dirV);

    Vector3d localStart = coord.GetLocalPoint(Vector3d(pArc2d->GetStartPoint(), 0));
    Vector3d localEnd = coord.GetLocalPoint(Vector3d(pArc2d->GetEndPoint(), 0));
    Vector3d localMid = coord.GetLocalPoint(Vector3d(pArc2d->GetMiddlePoint(), 0));

    OwnerPtr<SurfaceAndTerminals> opRet = NEW_AS_OWNER_PTR(SurfaceAndTerminals);
    opRet->SetDatumSurface__(ICylinder::Create(
        coord,
        Intervald(Constants::MATH_NEGINFINITY, Constants::MATH_INFINITY),
        IArc2d::Create(localStart.Vec2d(), localEnd.Vec2d(), localMid.Vec2d()).get())
    );

    const Intervald& rangeU = pArc2d->GetLimits();
    double midParam = 0.0;
    bool isParamInLimit = false;
    pArc2d->GetParameter(pArc2d->GetMiddlePoint(), midParam, isParamInLimit, Constants::LENGTH_EPS);
    opRet->SetGlobalTerminalParams(
        TerminalParams::Create(
            rangeU.GetMin(), midParam, rangeU.GetMax(),
            rangeV.GetMin(), rangeV.Middle(), rangeV.GetMax()
        )
    );

    return opRet;
}

bool gcmp::SurfaceAndTerminals::IsPlanar() const
{
    const ISurface* pSurfaceRef = this->GetSurfaceReference();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pSurfaceRef, L"pSurfaceRef为空。",L"GDMPLab",L"2024-03-30");
    return GeometryType::Plane == pSurfaceRef->GetType();
}

bool gcmp::SurfaceAndTerminals::IsCylindric() const
{
    const ISurface* pSurfaceRef = this->GetSurfaceReference();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pSurfaceRef, L"pSurfaceRef为空。",L"GDMPLab",L"2024-03-30");
    return GeometryType::Cylinder == pSurfaceRef->GetType();
}

bool gcmp::SurfaceAndTerminals::HasLocalTerminalParams() const
{
    const UMapLocalTerminalParams& localMap = this->GetLocalTerminalParams__();
    return !localMap.empty();
}

bool gcmp::SurfaceAndTerminals::HasLocalTerminalParams(ElementId modelviewId) const
{
    const UMapLocalTerminalParams& localMap = this->GetLocalTerminalParams__();
    return localMap.find(modelviewId) != localMap.end();
}

TerminalParams gcmp::SurfaceAndTerminals::GetLocalTerminalParams(ElementId modelviewId) const
{
    const UMapLocalTerminalParams& localMap = this->GetLocalTerminalParams__();
    auto iter = localMap.find(modelviewId);

    // 如果当前视图有视图特定参数，则返回视图特定参数
    if(iter != localMap.end())
    {
        return iter->second;
    }

    // 否则，返回全局参数
    return this->GetGlobalTerminalParams();
}

bool gcmp::SurfaceAndTerminals::AddOrUpdateLocalTerminalParams(ElementId modelviewId, const TerminalParams & params)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(modelviewId.IsValid(), L"modelviewId不合法。",L"GDMPLab",L"2024-03-30");
    UMapLocalTerminalParams& localMap = this->GetLocalTerminalParamsFW__();
    localMap[modelviewId] = params;

    return true;
}

bool gcmp::SurfaceAndTerminals::DeleteAllLocalTerminalParams()
{
    UMapLocalTerminalParams& localMap = this->GetLocalTerminalParamsFW__();
    localMap.clear();
    return true;
}

bool gcmp::SurfaceAndTerminals::DeleteLocalTerminalParams(ElementId modelviewId)
{
    UMapLocalTerminalParams& localMap = this->GetLocalTerminalParamsFW__();
    auto iter = localMap.find(modelviewId);
    if(iter == localMap.end())
    {
        return false;
    }

    localMap.erase(iter);

    return true;
}

bool gcmp::SurfaceAndTerminals::PerformTranform(const Matrix4d & mat)
{
    ISurface* pSurfaceRef = this->GetDatumSurfaceFW__().get();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pSurfaceRef, L"pSurfaceRef不合法。",L"GDMPLab",L"2024-03-30");
    return pSurfaceRef->Transform(mat);
}

OwnerPtr<ISurface> gcmp::SurfaceAndTerminals::GetSurface(const TerminalParams & params) const
{
    const ISurface* pSurfaceRef = this->GetSurfaceReference();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pSurfaceRef, L"pSurfaceRef为空。",L"GDMPLab",L"2024-03-30");

    bool bSameDirU = false, bSameDirV = false;
    Intervald rangeU = params.GetRangeU(bSameDirU);
    Intervald rangeV = params.GetRangeV(bSameDirV);

    OwnerPtr<ISurface> opRet = nullptr;

    if (this->IsPlanar())
    {
        const IPlane* pPlaneRef = dynamic_cast<const IPlane*>(pSurfaceRef);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPlaneRef, L"pPlaneRef为空。",L"GDMPLab",L"2024-03-30");
        opRet = IPlane::Create(
            pPlaneRef->GetOrigin(),
            pPlaneRef->GetDirectionU() * (bSameDirU ? 1.0 : -1.0),
            pPlaneRef->GetDirectionV() * (bSameDirV ? 1.0 : -1.0),
            rangeU * (bSameDirU ? 1.0 : -1.0),
            rangeV * (bSameDirV ? 1.0 : -1.0)
        );
    }
    else if (this->IsCylindric())
    {
        const ICylinder* pCylinderRef = dynamic_cast<const ICylinder*>(pSurfaceRef);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pCylinderRef, L"pCylinderRef为空。",L"GDMPLab",L"2024-03-30");
        OwnerPtr<IArc2d> opArc2d = pCylinderRef->GetArc2d();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opArc2d, L"opArc2d为空。",L"GDMPLab",L"2024-03-30");

        Vector2d newStartPt = opArc2d->GetPoint(params.GetStartRangeU());
        Vector2d newMiddlePt = opArc2d->GetPoint(params.GetMidRangeU());
        Vector2d newEndPt = opArc2d->GetPoint(params.GetEndRangeU());

        OwnerPtr<IArc2d> opNewArc2d = IArc2d::Create(newStartPt, newEndPt, newMiddlePt);

        Coordinate3d coord = Coordinate3d::CreateFromAxis(
            pCylinderRef->GetCoordinate().GetOrigin(),
            pCylinderRef->GetCoordinate().GetX(),
            pCylinderRef->GetCoordinate().GetY() * (bSameDirV ? 1.0 : -1.0)
        );

        opRet = ICylinder::Create(
            coord,
            rangeV * (bSameDirV ? 1.0 : -1.0),
            opNewArc2d.get()
        );
    }
    else
    {
        DBG_WARN(L"仅支持平面或圆柱面。",L"GDMPLab",L"2024-03-30");
    }

    return opRet;
}
